using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages.Communication;
using System.Web;
using System.Globalization;

using Core.ListViewObjectModel;

namespace Core
{
	/// <summary>
	/// Summary description for SPListUtil.
	/// </summary>
	public class SPListUtil
	{
		/// <summary>
		/// Gets the view (SPView) for the SharePoint list specified by the URL.
		/// </summary>
		/// <param name="viewUrl">URL for the listview.</param>
		/// <param name="context">HttpContext (if null, it's not used)</param>
		/// <returns>Listview (SPView)</returns>
		/// <remarks>First checks the current web, then checks other webs in current site collection.</remarks>
		public static SPView GetViewForUrl(string viewUrl, HttpContext context)
		{
			SPView retVal = null;

			if (context != null)
			{
				try
				{
					// Check if it exists in current web
					retVal = SPControl.GetContextWeb(context).GetViewFromUrl(viewUrl);
				}
				catch (System.ArgumentException) {}
				catch (SPException) {}
			}

			if (retVal == null)
			{
				// Check if it exists in any web in current site collection
				SPWeb web = GetWebForItemUrl(viewUrl);
				if (web != null)
				{
					try
					{
						retVal = web.GetViewFromUrl(viewUrl);
					}
					catch (System.ArgumentException) {}
					catch (SPException) {}
				}
			}

			return retVal;
		}

		/// <summary>
		/// Gets the view (SPView) for the SharePoint list in the specified sitecollection (SPSite) specified by the URL.
		/// </summary>
		/// <param name="viewUrl">URL for the listview.</param>
		/// <param name="site">Sitecollection to scan.</param>
		/// <returns>Listview (SPView)</returns>
		public static SPView GetViewForUrl(string viewUrl, SPSite site)
		{
			SPView retVal = null;

			foreach (SPWeb web in site.AllWebs)
			{
				try
				{
					retVal = web.GetViewFromUrl(viewUrl);
					break;
				}
				catch (System.ArgumentException) {}
				catch (SPException) {}
			}

			return retVal;
		}


		/// <summary>
		/// Get the sitecollection (SPSite) that contains the site (SPWeb) that contains the SharePoint object that is referenced by the specified url.
		/// </summary>
		/// <param name="fullItemUrl">Full URL of the SharePoint object</param>
		/// <returns>Sitecollection (SPSite)</returns>
		public static SPSite GetSiteForItemUrl(string fullItemUrl)
		{
			SPSite retVal = null;
			string url = fullItemUrl;
			while ((retVal == null) && (url.Length > 0))
			{
				try
				{
					retVal = new SPSite(url);
				}
				catch (System.UriFormatException)
				{
					url = ShortenUrl(url);
				}
				catch (SPException)
				{
					url = ShortenUrl(url);
				}
				catch (System.ArgumentException)
				{
					url = ShortenUrl(url);
				}
			}
			return retVal;
		}

		/// <summary>
		/// Get the site (SPWeb) that contains the SharePoint object that is referenced by the supplied URL.
		/// </summary>
		/// <param name="itemUrl">URL for the SharePoint object</param>
		/// <returns>Site (SPWeb)</returns>
		/// <remarks>First checks current sitecollection, then searches other sitecollections</remarks>
		public static SPWeb GetWebForItemUrl(string itemUrl)
		{
			SPWeb retVal = null;

			// Check if it can be found in any sitecollection
			SPSite site = GetSiteForItemUrl(itemUrl);
			if (site != null) // Site found for the url
			{
				retVal = GetWebForItemUrl(itemUrl, site);
			}

			return retVal;
		}

		/// <summary>
		/// Get the site (SPWeb) that contains the SharePoint object that is referenced by the supplied URL.
		/// </summary>
		/// <param name="itemUrl">URL for the SharePoint object</param>
		/// <param name="thisSite">Sitecollection (SPSite) to scan</param>
		/// <returns>Site (SPWeb)</returns>
		/// <remarks>Checks the specified sitecollection for the site that should contain the SharePoint object</remarks>
		public static SPWeb GetWebForItemUrl(string itemUrl, SPSite thisSite)
		{
			SPWeb retVal = null;
			string url = GetRelativeUrl(itemUrl);
			while ((retVal == null) && (url.Length > 0))
			{
				try
				{
					retVal = thisSite.AllWebs[url];
					string webUrl = retVal.Url; // Check if Url-property can be read. Eg: the Lists-subfolder can be opened as a SPWeb, while it isn't really a web
				}
				catch (SPException)
				{
					url = ShortenUrl(url);
					retVal = null;
				}
				catch (System.ArgumentException)
				{
					url = ShortenUrl(url);
					retVal = null;
				}
				catch (System.IO.FileNotFoundException)
				{
					url = ShortenUrl(url);
					retVal = null;
				}
			}
			return retVal;
		}

		#region ListViewObjectModel methods
		/// <summary>
		/// Creates a collection of values which together make up the value of a field.
		/// </summary>
		/// <param name="listViewField">ViewField</param>
		/// <param name="field">SPField</param>
		/// <param name="itemId">Id of the row</param>
		/// <param name="fieldValue">Orginal value</param>
		/// <returns></returns>
		private static ListViewRowValuePartCollection GetSPListItemValueParts(SPView view, ListViewField listViewField, SPField field, int itemId, object fieldValue)
		{
			ListViewRowValuePartCollection retVal = new ListViewRowValuePartCollection();
			
			switch (listViewField.FieldType)
			{
				case "Calculated": // Type unknown
					string rawCalcValue = (string)fieldValue;
					int posCalcSep = rawCalcValue.IndexOf(";#");
					retVal.Add(new ListViewRowValuePart(rawCalcValue.Substring(posCalcSep+2)));
					break;
				case "Lookup":
					if (fieldValue != null)
					{
						string rawLookupValue = (string)fieldValue;
						string lookupId = string.Empty;
						int posIdEnd = rawLookupValue.IndexOf(";#");
						if (posIdEnd >= 0)
						{
							lookupId = rawLookupValue.Substring(0,posIdEnd);
							rawLookupValue = rawLookupValue.Substring(posIdEnd+2);
							retVal.Add(new ListViewRowValuePart(rawLookupValue));
							Guid lookupListGuid = new Guid(((SPFieldLookup)field).LookupList);
							SPList lookupList;
							try
							{
								lookupList = view.ParentList.ParentWeb.Lists[lookupListGuid];
								retVal.Add(new ListViewRowValuePart(lookupList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url));
								retVal.Add(new ListViewRowValuePart(lookupId));
							}
							catch {} // Lookup-list not found or not accessible.
						}
						else
						{ // No lookup-id found
							retVal.Add(new ListViewRowValuePart(rawLookupValue));
						}
					}
					else
						retVal.Add(new ListViewRowValuePart());
					break;
				case "Counter": // auto-increment (ID-column)
					retVal.Add(new ListViewRowValuePart(String.Format("{0}",(int)fieldValue)));
					break;
				case "Number":
					retVal.Add(new ListViewRowValuePart(String.Format("{0}",(double)fieldValue)));
					break;
				case "DateTime":
					DateTime dt;
					try
					{
						dt = DateTime.Parse((string)fieldValue,CultureInfo.CurrentUICulture);
					}
					catch (Exception)
					{
						dt = (DateTime)fieldValue;
					}
					retVal.Add(new ListViewRowValuePart(String.Format("{0:d}", dt)));
					break;
				case "Boolean":
					bool rawBoolValue = (fieldValue != null) ? (bool)fieldValue : false;
					retVal.Add(new ListViewRowValuePart(String.Format("{0}", rawBoolValue)));
					break;
				case "User":
					if (fieldValue != null)
					{
						string userName = (string)fieldValue;
						string userId = string.Empty;
						int posIdEnd = userName.IndexOf(";#");
						if (posIdEnd >= 0)
						{
							userId = userName.Substring(0,posIdEnd);
							userName = userName.Substring(posIdEnd+2);
							retVal.Add(new ListViewRowValuePart(userName));
							retVal.Add(new ListViewRowValuePart(userId));
						}
						else
						{
							retVal.Add(new ListViewRowValuePart(userName));
						}
					}
					else
						retVal.Add(new ListViewRowValuePart());
					break;
				case "EDIT":
					// TODO: Find rootfolder and add as second param (and let xslt use it)
					//					string rootFolder = string.Empty;
					//					if (fieldValue is String)
					//					{
					//						rootFolder = (string)fieldValue;
					//					}
					//					retVal.Add(new ListViewRowValuePart(rootFolder));
					break;
				case "FILE":
					retVal.Add(new ListViewRowValuePart((string)fieldValue));
					break;
				case "DOCICON":
					// Find the name of the icon-image
					string iconUrl = SPUtility.MapToIcon(field.ParentList.ParentWeb, "MapToIcon."+(string)fieldValue, "");
					retVal.Add(new ListViewRowValuePart(iconUrl));
					break;
                case "Attachments":
                    retVal.Add(new ListViewRowValuePart(fieldValue.ToString()));
                    break;
				default: // String, Computed, etc.
					string fieldStringValue;
					try
					{
						fieldStringValue = (fieldValue != null) ? (string)fieldValue : string.Empty;
					}
					catch (NullReferenceException)
					{
						fieldStringValue = string.Empty;
					}
					if (fieldStringValue.IndexOf("://") > 0) // url in string
					{
						int posLinkSep = fieldStringValue.IndexOf(",");
						if (posLinkSep > 0) // Both link + description found
						{
							retVal.Add(new ListViewRowValuePart(fieldStringValue.Substring(0, posLinkSep))); // Link
							retVal.Add(new ListViewRowValuePart(fieldStringValue.Substring(posLinkSep+2))); // Descr
						}
						else
							retVal.Add(new ListViewRowValuePart(fieldStringValue));
					}
					else
						retVal.Add(new ListViewRowValuePart(fieldStringValue));
					break;
			}

			return retVal;
		}

		/// <summary>
		/// Generates a ListView-objectmodel for the specified SPView
		/// </summary>
		/// <param name="view">SPView</param>
		/// <returns>Instance of an ListView-objectmodel containing the definition and data of the SPView.</returns>
		public static Core.ListViewObjectModel.ListView GenerateListViewObjectModel(SPView view)
		{
            Core.ListViewObjectModel.ListView listView = new Core.ListViewObjectModel.ListView();
			SPList parentList = view.ParentList;

			#region Set properties
			listView.AbsoluteUrl = parentList.ParentWeb.Url + view.Url;
			listView.BaseType = parentList.BaseType;
			listView.DisplayName = parentList.Title;
			listView.ListID = parentList.ID.ToString();
			listView.RowLimit = Convert.ToInt32(view.RowLimit);
			listView.SiteUrl =  parentList.ParentWeb.Url + "/";
			listView.ViewID = view.ID.ToString();
			listView.DisplayFormUrl = parentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url;
			listView.NewFormUrl = parentList.Forms[PAGETYPE.PAGE_NEWFORM].Url;
			listView.EditFormUrl = parentList.Forms[PAGETYPE.PAGE_EDITFORM].Url;
			//			listView.RootUrl = parentList.ParentWeb.Url.Substring(parentList.ParentWeb.Url.Length - parentList.RootFolder.Url.Length) + "/";
			listView.RootUrl = parentList.ParentWeb.Url + "/" + parentList.RootFolder.Url + "/";
			#endregion

			#region Fill fields
			foreach (string viewFieldName in view.ViewFields)
			{
				SPField listField = parentList.Fields.GetFieldByInternalName(viewFieldName);
				ListViewField field = new ListViewField();
				field.DisplayName = listField.Title;
				field.Name = viewFieldName;				
				field.FieldType = listField.TypeAsString;
				field.LinkToItem = false;
				
				// Translate fieldtype for computed fields
				if (listField.TypeAsString.Equals("Computed"))
				{
					switch (listField.InternalName.ToUpper())
					{
						case "TITLE":
							field.FieldType = "Text";
							field.LinkToItem = true;
							break;
						case "URLWMENU":
							field.FieldType = "URL";
							//Impossible, since it's already clickable.
							//field.LinkToItem = true;
							break;
						case "LINKTITLENOMENU":
							field.LinkToItem = true;
							break;
						case "LINKTITLE":
							field.LinkToItem = true;
							break;
						case "LINKFILENAME":
							field.FieldType = "FILE";
							field.LinkToItem = true;
							break;
						case "LINKCHECKEDOUTTITLE":
							field.FieldType = "User";
							break;
						case "DOCICON":
							field.FieldType = "DOCICON";
							field.LinkToItem = true;
							break;
						case "EDIT":
							field.FieldType = "EDIT";
							break;
					}
				}

				if (listField.InternalName.ToUpper() == "LAST_X0020_MODIFIED")
				{
					field.FieldType = "DateTime";
				}

				field.Hidden = listField.Hidden;
				listView.Fields.Add(field);
			}
			#endregion

			// Get contents of the view of the list
			SPListItemCollection listItems = parentList.GetItems(view);

			// Fill rows
			foreach(SPListItem item in listItems)
			{
				int itemId = (int)item["ID"]; // Each listitem has an ID-field.
				ListViewRow row = new ListViewRow();
				row.Id = itemId;

				for (int fieldCount=0;fieldCount<listView.Fields.Count;fieldCount++)
				{
					ListViewField field = listView.Fields[fieldCount];
					ListViewRowValue rowValue = new ListViewRowValue(field.Name);
					ListViewRowValuePartCollection rowValueParts = GetSPListItemValueParts(view, field, parentList.Fields.GetFieldByInternalName(field.Name), itemId, item[field.Name]);
					rowValue.Parts = rowValueParts;
					row.RowValues.Add(rowValue);
				}				
				listView.Rows.Add(row);
			}
			return listView;
		}
		#endregion


		/// <summary>
		/// Cuts of the part of the url after the last \ or /.
		/// </summary>
		/// <param name="url"></param>
		/// <returns>Url</returns>
		private static string ShortenUrl(string url)
		{
			// /a/b/c.aspx
			char[] slashes = {'/', '\\'};
			int posSlash = url.LastIndexOfAny(slashes);
			if (posSlash > 0)
			{
				return url.Substring(0, posSlash);
			}
			else if ((posSlash == 0) && (url.Length != 1))
			{
				return "/";
			}
			else
				return string.Empty;
		}

		/// <summary>
		/// Returns the server-relative url for the specified url (http://a/b/c.htm results in /b/c.htm).
		/// </summary>
		/// <param name="fullUrl">Full url</param>
		/// <returns>Server-relative url</returns>
		private static string GetRelativeUrl(string fullUrl)
		{
			string retVal = fullUrl;
			if (fullUrl.ToLower().StartsWith("http"))
			{
				int posPathStart = retVal.Substring(8).IndexOf("/");
				if (posPathStart >= 0)
					retVal = retVal.Substring(posPathStart+8);
				else
					retVal = "/";
			}
			return retVal;
		}
	}
}
